import time
import re
from collections import Counter

t1 = time.time()


def extract_and_merge_duplicates(TZ_list, input_string, JL_ZL, target_float):
    JL_ZL = float(JL_ZL)
    list_pattern = re.compile(r'\[(.*?)\]')
    match = list_pattern.search(str(input_string))

    if match:
        list_str = match.group(1)
        try:
            items = [float(item) for item in eval(list_str)]  # 注意：使用eval存在安全风险，请确保输入受信任
        except:
            pass

        counter = Counter(items)

        output_items = []
        now_use = False
        for item, count in counter.items():
            merged_item_value = item * count
            calculated_value = (merged_item_value / target_float) * JL_ZL

            if count != 1:
                output_items.append(f"{item}*{count}({calculated_value:.2f}吨)")
            else:
                output_items.append(f"{item}({calculated_value:.2f}吨)")

        if TZ_list != []:
            nopass_num = False
            pass_num = False
            for TZ_line in TZ_list:
                for out_line in output_items:
                    out_item = out_line.split("(")[0].split("*")[0]
                    out_num = out_line.split("(")[1].split(")")[0].strip("吨")
                    if float(TZ_line[0]) == float(out_item):
                        if float(TZ_line[1]) < float(out_num):
                            nopass_num = True
                        if float(TZ_line[1]) > float(out_num):
                            pass_num = True
            if nopass_num == False and pass_num == True:
                return ""

            output_string = "  组合：" + ", ".join(output_items)
            return output_string

        output_string = "  组合：" + ", ".join(output_items)
        return output_string
    else:
        return ""


def check_tz_condition(tz_list, comb, multiplier):
    for tz_weight, tz_value in tz_list:
        tz_weight_int = int(tz_weight * multiplier)
        if tz_weight_int in comb:
            index = comb.index(tz_weight_int)
            comb_weight = float(comb[index]) / multiplier
            if not (0.8 <= comb_weight / tz_value <= 1.2):
                return False
    return True

def knapsack_closest_sums(TZ_list,JL_ZL,weights, target_float, max_threshold, min_threshold, ts_line, dao_num):
    multiplier = 100
    weights = [int(weight * multiplier) for weight in weights]
    target = int(target_float * multiplier)
    max_threshold = int(max_threshold * multiplier)
    min_threshold = int(min_threshold * multiplier)

    n = len(weights)
    dp = [float('-inf')] * (target + 1)
    dp[0] = 0
    combinations = {0: [[]]}

    for i in range(n):
        for j in range(target, -1, -1):
            if j >= weights[i] and dp[j - weights[i]] != float('-inf'):
                current_sum = dp[j - weights[i]] + weights[i]
                if abs(current_sum - target) < abs(dp[j] - target):
                    dp[j] = current_sum
                    new_combinations = [tuple(sorted(list(comb) + [weights[i]])) for comb in combinations[j - weights[i]] if len(comb) + 1 <= dao_num]
                    combinations[j] = set(new_combinations)
                elif abs(current_sum - target) == abs(dp[j] - target):
                    new_combinations = [tuple(sorted(list(comb) + [weights[i]])) for comb in combinations[j - weights[i]] if len(comb) + 1 <= dao_num]
                    combinations[j].update(new_combinations)

    valid_combinations_by_difference = {}
    for j in range(target + 1):
        if dp[j] != float('-inf'):
            sum_value = dp[j]
            difference = abs(sum_value - target)
            if difference <= max_threshold and difference >= min_threshold:
                #sorted_combinations = sorted(combinations[j], key=lambda comb: sum(comb))
                sorted_combinations = sorted([comb for comb in combinations[j] if len(comb) <= dao_num], key=lambda comb: sum(comb))
                if difference not in valid_combinations_by_difference:
                    valid_combinations_by_difference[difference] = []
                valid_combinations_by_difference[difference].extend(sorted_combinations)

    output_lines = []
    if valid_combinations_by_difference:
        output_lines.append("满足条件的组合如下（按差值分组并按总和排序列出）：\n")
        for difference, combs in sorted(valid_combinations_by_difference.items(), key=lambda item: item[0]):
            original_difference = difference / multiplier
            difference_str = f"{original_difference:.2f}" if not original_difference.is_integer() else f"{int(original_difference)}"
            output_lines.append(f"当前组合：{target_float-float(difference_str)} 差值: {difference_str}")
            for comb in combs:
                print(TZ_list,comb)
                input()
                if not check_tz_condition(TZ_list, comb, multiplier):
                    continue  # 如果条件不满足，跳过当前组合
                float_comb = [weight / multiplier for weight in comb]
                int_float_comb = [int(x) if x.is_integer() else x for x in float_comb]
                output_lines.append(f"  组合: {int_float_comb}")
            output_lines.append("\n")
    else:
        output_lines.append("没有找到满足条件的组合。")

    new_output_lines = []
    if ts_line:
        for line in output_lines:
            if "组合:" in line:
                for num in ts_line:
                    if num in line:
                        line=extract_and_merge_duplicates(TZ_list,line, JL_ZL, target_float)
                        if (line!=""):
                            #print(line)
                            new_output_lines.append(line)
            else:
                if (line!=""):
                    new_output_lines.append(line)
    else:
        for line in output_lines:
            if "组合:" in line:
                line=extract_and_merge_duplicates(TZ_list,line, JL_ZL, target_float)
                if (line!=""):
                    #print(line)
                    new_output_lines.append(line)
            else:
                if (line!=""):
                    #print(line)
                    new_output_lines.append(line)
        #new_output_lines = output_lines

    with open("./new_num.txt", "w" ,encoding="utf-8") as f:
        f.writelines("\n".join(new_output_lines))

################## main ##################
JL_ZL=input("请输入卷料重量：")
print("开始计算...")
try:
    with open("./num.txt", "r",encoding="utf-8") as f:
        lines = f.readlines()

    weights = []
    ts_line = []
    new_weights = []
    max_countlist=[]
    TZ_list=[]
    exittime=5
    target_float = 1250.0
    max_threshold = 4.0
    min_threshold = 2.0
    dao_num=10

    for line in lines:
        if(line=="" or line==" " or line=="\n"):
            continue
        if "[原卷宽度]" in line:
            try:
                target_float=float(line.split("=")[1])
            except:
                print("[原卷宽度]输入有误")
                input()
            continue
        if "[最大边丝]" in line:
            try:
                max_threshold=float(line.split("=")[1])
            except:
                print("[最大边丝]输入有误")
                input()
            continue
        if "[最小边丝]" in line:
            try:
                min_threshold=float(line.split("=")[1])
            except:
                print("[最小边丝]输入有误")
                input()
            continue
        if "[限制刀数]" in line:
            try:
                dao_num=float(line.split("=")[1])
            except:
                print("[限制刀数]输入有误")
                input()
            continue
        if "#" in line:
            line = line.lstrip("#")
            ts_line.append(line.split("\t")[0].strip("\n"))
        stripped_line = line.strip()
        parts = stripped_line.split('\t')
        if(len(parts)>1):
            TZ_ADD=False
            for line in range(len(TZ_list)):
                if float(parts[0]) == TZ_list[line][0]:
                    TZ_list[line][1]=TZ_list[line][1]+float(parts[1])
                    TZ_ADD=True
                    break
            if(TZ_ADD==False):
                TZ_list.append([float(parts[0]),float(parts[1])])
        if(len(parts)>2):
            if [float(parts[0]),int(parts[2])] not in max_countlist:
                max_countlist.append([float(parts[0]),int(parts[2])])

        weight = float(parts[0])
        weights.append(weight)


    # 去除weights中所有的重复值
    weights = list(set(weights))

    # 创建一个新的weights，命名为new_weights
    for weight in weights:
        temp_target = target_float
        max_count=99999
        for num_line in max_countlist:
            if (weight == num_line[0]):
                max_count=num_line[1]
                break
        count=0
        while True:
            if(count>=max_count):
                break
            if(temp_target - weight >= 0):
                new_weights.append(weight)
                temp_target -= weight
                count+=1
            else:
                break

    # 然后将new_weight传入函数  
    knapsack_closest_sums(TZ_list,JL_ZL,new_weights, target_float, max_threshold, min_threshold, ts_line, dao_num)


except FileNotFoundError:
    print("文件 num.txt 未找到。")

t2=time.time()
print(f"结果已写入 new_num.txt,耗时:{(t2-t1):.2f}秒")
for i in range(exittime,0,-1):
    print(f"{i} 秒后自动退出...")
    time.sleep(1)
